//methods to test the functions in word64

#include "test_word64.h"

//main method
//calls the other methods
//return true if all test pass
//false if one of the tests fail
bool test_word64(){
  std::cout << "Testing word64" << std::endl;
  
  //test the different constructors and 
  //how to convert into a string
  bool status = test_w64_constructAndToString();
  if(status==false)
    return status;
  std::cout << "Passed: constructor and toString\n";
  
  //test getBit
  status=test_get();
  if(status==false)
    return false;
  std::cout<< "Passed: getBit()\n";
  
  //test setBit
  status=test_set();
  if(status==false)
    return false;
  std::cout<< "Passed: setBit()\n";
  
  //test clearBit
  status=test_clear();
  if(status==false)
    return false;
  std::cout<< "Passed: clearBit()\n";
 
  //test setVal
  status=test_val();
  if(status==false)
    return false;
  std::cout<< "Passed: setVal()\n";
  
  //test logical operators
  status=test_op();
  if(status==false)
    return false;
  std::cout<< "Passed: test_op()\n";

  //test logical operators
  status=test_arithmetic();
  if(status==false)
    return false;
  std::cout<< "Passed: test_arithmetic()\n";
  
  //test shift operators
  status=test_shift();
  if(status==false)
    return false;
  std::cout<< "Passed: test_shift()\n";
  
  //test weight
  status=test_weight();
  if(status==false)
    return false;
  std::cout<< "Passed: test_weight()\n";
  
  //test equal
  status=test_equal();
  if(status==false)
    return false;
  std::cout<< "Passed: test_equal()\n";
  
  //test assign
  status=test_w64_assign();
  if(status==false)
    return false;
  std::cout<< "Passed: test_assign()\n";
  
  //return true when all tests passed
  return true;
}

bool test_w64_constructAndToString(){
  word64 w1;
  //when using the default constructor the 0-word should be created
  std::string refString="0000000000000000";
  std::string testString=w1.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failing testing default constructor\n";
    return false;
  }
  //create the following word 0xca8643211234abcd
  refString="CA8643211234ABCD";
  word64 w2(0xca8643211234abcduLL);
  //test all four different toString 
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed when testing constructor from unsigned\n";
    return false;
  }
  refString="B3D52C4884C26153";
  testString=w2.toHexStringLSB();
  if(testString!=refString){
    std::cout << testString ;
    std::cout << "Failed: toHexStringLSB\n";
    return false;
  }
  refString="1100101010000110010000110010000100010010001101001010101111001101";
  testString=w2.toBinStringMSB();
  if(testString!=refString){
    std::cout << "Failed: toBinStringMSB\n";
    return false;
  }
  refString="1011001111010101001011000100100010000100110000100110000101010011";
  testString=w2.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed: toBinStringLSB\n";
    return false;
  }

  //test constuctor that takes another word64
  word64 w3(w2);
  //if it works then w3 will pass same test as w2 did
  w2.setBit(2);//modify w2 to check that w3 does not point to same data
  testString=w3.toBinStringLSB();
 
  if(testString!=refString){
    std::cout << "Failed: Constructor using word64\n";
    return false;
  }

  //test constructor that takes a string as input
  std::string s1="11000110";
  refString="0000000000000063";
  word64 w4(s1);
  testString=w4.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Constructor using string\n";
    return false;
  }
  s1+="111100111100000001011010";
  word64 w5(s1);
  refString="000000005A03CF63";
  testString=w5.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Constructor using string\n";
    return false;
  }
  s1+="11000011010110101110011100011001";
  word64 w6(s1);
  refString="98E75AC35A03CF63";
  testString=w6.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Constructor using string\n";
    return false;
  }
  //return true if all test cases pass
  return true;
  
}

//test getBit
bool test_get(){
  //create a 64-bit word with known pattern
  //extract bits with getBit and create testString
  //if testString equal refString then passed test
  std::string refString="1000101011001111011000111110101100111100101010100101010101111111";
  word64 w(0x8ACF63EB3CAA557FuLL);
  std::string testString="";
  for(int i=63;i>=0;i--){
    if(w.getBit(i)==0)
      testString+='0';
    else
      testString+='1';
  }
  if(testString!=refString){
    std::cout << testString << std::endl << refString << std::endl;
    std::cout << "Failed: getBit\n";
    return false;
  }
  return true;
}

//test setBit
bool test_set(){
  word64 w(0x5555000000005555uL);
  std::string refString="5555555555555555";
  //set all bits at even positions
  for(int i=0;i<64;i+=2)
    w.setBit(i);
  std::string testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: setBit\n";
    return false;
  }
  //set all bit to 1
  for(int i=0;i<64;i++)
    w.setBit(i);
  refString="FFFFFFFFFFFFFFFF";
  testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: setBit\n";
    return false;
  }
  return true;
}


//test clearBit
bool test_clear(){
  word64 w(0xFFFF5555FFFF5555uL);
  std::string refString="AAAA0000AAAA0000";
  //clear all bits at even positions
  for(int i=0;i<64;i+=2)
    w.clearBit(i);
  std::string testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: clearBit\n";
    return false;
  }
  //clear all bits 
  for(int i=0;i<64;i++)
    w.clearBit(i);
  refString="0000000000000000";
  testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: clearBit\n";
    return false;
  }

  return true;
}

//test setVal
bool test_val(){
  std::string refString="AAAAAAAAAAAAAAAA";
  word64 w(0xffaa55000055aaffuLL);
  //set even bits to 0 and odd bits to 1
  for(int i=0;i<64;i++)
    w.setVal(i,i&1);
  std::string testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: setVal\n";
    return false;
  }
  return true;
}

//test logical operators
bool test_op(){
  word64 w1(0x5555AAAAAAAA5555uLL);
  word64 w2(0x55AA55AA55AA55AAuLL);
  //test and
  std::string refString="550000AA00AA5500";
  word64 wAnd(w1);
  wAnd.andOp(w2);
  std::string testString=wAnd.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (and)\n";
    return false;
  }
  //test or
  refString="55FFFFAAFFAA55FF";
  word64 wOr(w1);
  wOr.orOp(w2);
  testString=wOr.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (or)\n";
    return false;
  }
  //test xor
  refString="00FFFF00FF0000FF";
  word64 wXor(w1);
  wXor.xorOp(w2);
  testString=wXor.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (xor)\n";
    return false;
  }
  //test not
  refString="AAAA55555555AAAA";
  word64 wNot(w1);
  wNot.notOp();
  testString=wNot.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (not)\n";
    return false;
  }
  return true;
}

//test arithmetic
bool test_arithmetic(){
  word64 w1(0xCCCCCCCCCCCCCCCCuLL);
  word64 w2(0x81C043210CCABBA7uLL);
  //test add
  word64 wAdd(w1);
  wAdd.add(w2);
  std::string refString="4E8D0FEDD9978873";
  std::string testString=wAdd.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_arithmetic (add)" << std::endl;
    return false;
  }
  //test sub without carry high bit
  word64 wSub(w1);
  wSub.sub(w2);
  refString="4B0C89ABC0021125";
  testString=wSub.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_arithmetic (sub)" << std::endl;
    return false;
  }
  //test sub with carry high bit
  word64 wSubc(w2);
  wSubc.sub(w1);
  refString="B4F376543FFDEEDB";
  testString=wSubc.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_arithmetic (sub with carry)" << std::endl;
    return false;
  }
  return true;
}

//test shift operators
bool test_shift(){
  word64 w(0x842137EF045741EFuLL);
  //test left shift
  word64 wL(w);
  wL.lshift(12);
  std::string refString="137EF045741EF000";
  std::string testString=wL.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_shift (left shift)" << std::endl;
    return false;
  }
  //test right shift
  word64 wR(w);
  wR.rshift(8);
  refString="00842137EF045741";
  testString=wR.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_shift (right shift)" << std::endl;
    return false;
  }
  //test circular shift
  word64 wC(w);
  wC.cshift(48);
  refString="41EF842137EF0457";
  testString=wC.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_shift (circular shift)" << std::endl;
    return false;
  }
  return true;
}


//test weight
bool test_weight(){
  //test word with all 0
  word64 w1(0);
  if(w1.weight()!=0){
    std::cout << "Failed: weight (0)\n";
    return false; 
  }
  //test word with all 1
  word64 w2(0xffffffffffffffffuLL);
  if(w2.weight()!=64){
    std::cout << "Failed: weight (1)\n";
    return false; 
  }
  //test a third word
  word64 w3(0xf0123abccba3210fuLL);
  if(w3.weight()!=30){
    std::cout << "Failed: weight (f0123abccba3210f)\n";
    return false; 
  }
  return true;
}

//test equal
bool test_equal(){
  word64 w1(0x123456789abcdef0uLL);
  word64 w2(w1);
  //test words that are equal
  if(!w1.equal(w2)){
    std::cout << "Failed: equal (equality)\n";
    return false;
  } 
  //test words that are inequal
  word64 w3(0x5436abab9abcdef0uLL);
  if(w1.equal(w3)){
    std::cout << "Failed: equal (inequality)\n";
    return false;
  } 
  return true;
}

//test assign
bool test_w64_assign(){
  word64 w1(0xca864321ca864321uLL);
  word64 w2(0x12345678ca864321uLL);
  w2.assign(w1);
  //test assign with another word
  std::string refString="CA864321CA864321";
  std::string testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed when testing assign with another word\n";
    return false;
  }
  //test assign with an unsigned integer
  refString="84C2615384C26153";
  w2.assign(0x84C2615384C26153ULL);
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: testing assign with unsigned long\n";
    return false;
  }
  //test assign with string
  std::string s1="11000110";
  refString="0000000000000063";
  w2.assign(s1);
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Assign with string\n";
    return false;
  }
  s1+="11110011110000000101101000010010001101000101011001110011";
  w2.assign(s1);
  refString="CE6A2C485A03CF63";
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Assign with string\n";
    return false;
  }

  //return true if all test cases pass
  return true;
}
